package route

import (
	"context"
	"github.com/gorilla/mux"
	"log"
	"net/http"
	"os"
	"os/signal"
	"strconv"
	"time"
)

type HttpRouter interface {
	SetPort(port int)
	SetTimeoutSec(timeout int)

	Get(path string, f HandlerFunc)
	Post(path string, f HandlerFunc)
	Patch(path string, f HandlerFunc)
	Put(path string, f HandlerFunc)
	Delete(path string, f HandlerFunc)

	Static(prefixPath, stripPrefix string, fs http.FileSystem)

	ListenAndServe()
}

type defaultHttpRouter struct {
	port, timeout int
	router        *mux.Router
}

func (this *defaultHttpRouter) Static(prefixPath, stripPrefix string, fs http.FileSystem) {
	this.router.PathPrefix(prefixPath).Handler(http.StripPrefix(stripPrefix, http.FileServer(fs)))
}

func (this *defaultHttpRouter) SetPort(port int) {
	this.port = port
}

func (this *defaultHttpRouter) SetTimeoutSec(timeout int) {
	this.timeout = timeout
}

func (this *defaultHttpRouter) Get(path string, f HandlerFunc) {
	this.addRoute(path, "GET", f)
}

func (this *defaultHttpRouter) Post(path string, f HandlerFunc) {
	this.addRoute(path, "POST", f)
}

func (this *defaultHttpRouter) Patch(path string, f HandlerFunc) {
	this.addRoute(path, "PATCH", f)
}

func (this *defaultHttpRouter) Put(path string, f HandlerFunc) {
	this.addRoute(path, "PUT", f)
}

func (this *defaultHttpRouter) Delete(path string, f HandlerFunc) {
	this.addRoute(path, "DELETE", f)
}

func NewHttpRouter() HttpRouter {
	return &defaultHttpRouter{
		port:    8080,
		timeout: 60,
		router:  mux.NewRouter(),
	}
}

func (this *defaultHttpRouter) addRoute(path, method string, f HandlerFunc) {
	this.router.HandleFunc(path, func(writer http.ResponseWriter, request *http.Request) {
		ctx := NewContext(writer, request)
		f(ctx)
	}).Methods(method)
}

func (this *defaultHttpRouter) ListenAndServe()  {
	server := &http.Server{
		Addr: ":" + strconv.Itoa(this.port),
		Handler: this.router,
		ReadTimeout: time.Minute,
		WriteTimeout: time.Minute,
	}
	go func() {
		log.Printf("start http server successful, port: %d ...", this.port)
		if err := server.ListenAndServe(); err != nil {
			log.Println("start http server error,", err)
		}
	}()

	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)
	<-c
	ctx, cancel := context.WithTimeout(context.Background(), time.Second * 30)
	defer cancel()
	server.Shutdown(ctx)
	log.Println("stop http server")
}
